import {TreeNode} from './TreeNode.js'
import {Subject} from 'rxjs/Subject'
class Dimension extends TreeNode {

    constructor(key, value = null, space = null, accurators = null) {
        super(value)
        this.dtype = ""
        this.dtypes = ['string', 'int', 'float', 'object', 'list']

        this.key = key //维度名称
        this.value = value //事件
        this.default = null //默认值
        this.space = space //样本空间

        this.accurators = null //事件切换执行器
        this.spaceFilters = [] //空间过滤器

        //事件值转换器
        this.valueTransformer = {
            get: null,
            set: null,
        }

        this.prepareSubject()
    }

    /* 这是仿照jquery的 $语法，返回树结构下的某个节点
    @child 需要遍历的根节点 
    @query 查询语法 ； 例如 "canvas.top" , 或者 "canvas.widgets[0].x" 
    */
    $(child, query) {
        let self = this
        let queries = query.split('.') //根据.拆分字符串
        let first = queries[0]
        if (queries.length == 1) { //如果拆分 . 后，长度==1,则是查询语法的最底层
            let children = child.children
            if (children.length == 1) {
                return children[0]
            } else {
                let result
                children.forEach(element => {
                    if (element.key == first.split('[')[0]) {
                        result = element
                    }
                });
                return self._getMatchIndex(first, result)
            }
        } else { //如果拆分 . 后，长度 >1 ,则还需要使用当前的函数进行递归处理

            let children = child.children
            let selectedChild = null
            children.forEach(item => {
                if (item.key == first.split('[')[0]) { 
                    selectedChild = item
                }
            });
            let next = self._getMatchIndex(first, selectedChild)
            return this.$(next, query.replace(first + '.', ''))
        }
    }

    /* 检查查询中是否包含[0]这样的取下标语法，返回相应的子树 */
    _getMatchIndex(query, list) {
        if (query.indexOf('[') > -1) { //如果是取数组的索引值
            let index = -1
            var reg = /(?<=\[).+(?=\])/;
            var matchResult = query.match(reg);
            if (matchResult.length > 0) {
                let matchIndex = matchResult[0]
                index = parseInt(matchIndex)
            }
            return list.children[index]
        } else {
            return list
        }
    }

    /* 创建事件驱动的主题
      1 值改变
      2 样本空间改变
      */
    prepareSubject() {
        this.valueChangeSubject = new Subject();
        this.spaceChangeSubject = new Subject();
        this.infoSubject = new Subject();
    }

    setValue() {
        console.log('请根据不同节点类型实现');
    }


    getValue() {
        if (this.valueTransformer.get) {
            return this.valueTransformer.get(this.value)
        } else {
            return this.value
        }
    }

    setSpace(space) {
        let oldSpace = this.space
        this.space = space

        let signal = {
            key: this.key,
            oldValue: oldSpace,
            newValue: this.space,
            root: this.parent
        }
        this.spaceChangeSubject.next(signal)
        this.infoSubject.next(signal)
    }

    getSpace() {

    }

    /* 下级维度叉积器
    此函数用于分析多维相关事件的所有可能性
    虽然多维度之间的事件不一定有关联，但有关联的事件如果需要有执行逻辑，需要特别声明一个执行函数

    生成叉积一个重要原因是要进行全量的代码测试，以保证所有情况都有覆盖到。
    一般情况下，人为描述的叉积场景都有可能会遗漏，所以需要在代码层进行生成，并人为审核

    @dimenstionList 需要进行叉积的维度名
    */
    getCrossValues(dimenstionList) {
        let array = dimenstionList
        let result = []
        let tmp = []
        let leftSpace = null
        for (let i = 0; i < array.length; i++) {
            const leftIndex = array[i];
            let leftNode = this.findNodeByKey(leftIndex)
            if (i == 0) {
                leftSpace = this.copyData(leftNode.space)
            } else {
                leftSpace = result
            }
            if (i < array.length - 1) {
                let rightIndex = array[i + 1]
                let rightNode = this.findNodeByKey(rightIndex)
                let rightSpace = this.copyData(rightNode.space)
                for (let j = 0; j < leftSpace.length; j++) {
                    const jelement = (i == 0 ? leftIndex + ' == ' : '') + leftSpace[j].toString();
                    for (let k = 0; k < rightSpace.length; k++) {
                        const kelement = rightIndex + ' == ' + rightSpace[k];
                        leftSpace[j] = jelement + ' && ' + kelement
                        tmp.push(leftSpace[j])
                    }
                }
                result = tmp
                tmp = []
            } else {
                //不需要再继续处理
            }

        }
        return result
    }

    copyData(obj) {
        return JSON.parse(JSON.stringify(obj))
    }

    fromJsonString(jsonString) {
        let jsonObject = JSON.parse(jsonString)
        this.fromJsonObject(jsonObject)
    }

    fromJsonObject(dataToProcess) {
        let self = this
        if (Array.isArray(dataToProcess)) {
            dataToProcess.forEach((element, i) => {
                let elementNode
                if (typeof (element) == 'object') {
                    if (Array.isArray(element)) {
                        elementNode = new ListDimension(i, element)
                    } else {
                        elementNode = new ObjectDimension(i, element)
                    }
                } else {
                    elementNode = new LeafDimension(i, element)
                }
                self.addChild(elementNode)
            });
        } else {
            this.processObjectDType(dataToProcess)
        }
    }

    toJson() {
        return null
    }

    processObjectDType(objectInstance) {
        let self = this
        for (const key in objectInstance) {
            if (Object.hasOwnProperty.call(objectInstance, key)) {
                let elementNode
                let element = objectInstance[key];
                if (typeof (element) == 'object') {
                    if (Array.isArray(element)) {
                        elementNode = new ListDimension(key, element)
                    } else {
                        elementNode = new ObjectDimension(key, element)
                    }
                } else {
                    elementNode = new LeafDimension(key, element)
                }
                self.addChild(elementNode)
            }
        }
    }

    processListDType(list) {
        let self = this
        list.forEach((element, i) => {
            let elementNode
            if (typeof (element) == 'object') {
                if (Array.isArray(element)) {
                    elementNode = new ListDimension(i, element)
                } else {
                    elementNode = new ObjectDimension(i, element)
                }
            } else {
                elementNode = new LeafDimension(i, element)
            }
            self.addChild(elementNode)
        });
        this.length = list.length
    }


    //找到相应值的节点
    findNodeByKey(key) {
        let root = this.getRoot() //找到root
        let list = root.traverseInOrderByList() //使用root遍历
        let result = null
        list.forEach(node => {
            if (node.key == key) {
                result = node
            }
        });
        return result
    }

    generateNewData(key, obj) {
        let elementNode
        if (typeof (obj) == 'object') {
            if (Array.isArray(obj)) {
                elementNode = new ListDimension(key, obj)
            } else {
                elementNode = new ObjectDimension(key, obj)
            }
        } else {
            elementNode = new LeafDimension(key, obj)
        }
        return elementNode
    }
}

class LeafDimension extends Dimension {
    constructor(key, value = null, space = null, accurators = null) {
        super(key, value, space, accurators)
        this.dtype = 'leaf'
    }
    prepareSubject() {
        this.valueChangeSubject = new Subject();
        this.spaceChangeSubject = new Subject();
        this.infoSubject = new Subject();
    }
    setValue(value, headers) {
        // console.log('setValue',this.key, value);
        let self = this
        let oldValue = this.value
        this.value = value
        let notifyData = { //通知事件
            key: null,
            oldValue: oldValue,
            newValue: value,
            headers: headers
        }
        self.valueChangeSubject.next(notifyData)
        // self.infoSubject.next(notifyData)
        return
    }

    toJson() {
        return this.value
    }
}

class ObjectDimension extends Dimension {
    constructor(key, value, space = null, accurators = null) {
        super(key, value, space, accurators)
        this.dtype = 'object'
        this.processObjectDType(value)
    }

    prepareSubject() {
        this.valueChangeSubject = new Subject();
        this.valueDeleteSubject = new Subject();

        this.propertyAddSubject = new Subject();
        this.propertyUpdateSubject = new Subject();
        this.propertyDeleteSubject = new Subject();

        this.spaceChangeSubject = new Subject();
        this.infoSubject = new Subject();
    }

    push(id, obj) {
        let self = this
        let children = this.children
        let newData = this.generateNewData(id, obj)
        this.children.push(newData)
        this.length = children.length
        let notifyData = { //通知事件
            push: {
                id: id,
                obj: obj
            }
        }
        self.pushSubject.next(notifyData)
        self.infoSubject.next(notifyData)
    }

    /* 根据是否包含key值，判断是添加属性还是更新属性 */
    updateOrAddProperty(key, obj) {
        let isIn = false
        let index = -1
        if (!this.children) {
            this.children = []
        }
        let children = this.children
        children.forEach((child, i) => {
            if (child.key == key) {
                isIn = true
                index = i
            }
        });
        let newData = this.generateNewData(key, obj)
        // 判断类型
        if (index != -1) {
            this.updateProperty(index, 1, newData) //更新属性
        } else {
            this.addProperty(key, obj) //添加属性
        }
    }
    /* 添加属性 */
    addProperty(key, obj) {
        let self = this
        if (!this.children) {
            this.children = []
        }
        let children = this.children
        let newData = this.generateNewData(key, obj)
        this.children.push(newData)
        this.length = children.length
        let notifyData = { //通知事件
            key: key,
            newValue: obj,
        }
        self.propertyAddSubject.next(notifyData)
        self.infoSubject.next(notifyData)
    }
    /* 更新属性 */
    updateProperty() { //动态参数
        let self = this
        this.children.splice(...arguments)
        let notifyData = { //通知事件
            key: arguments[2].key,
            index: arguments[0],
            newValue: arguments[2],
            splice: [...arguments]
        }
        self.propertyUpdateSubject.next(notifyData)
        self.infoSubject.next(notifyData)

    }
    /* 删除属性 */
    deleteProperty(key) {
        let self = this
        let children = this.children
        let indexToDelete = -1
        for (let i = 0; i < children.length; i++) {
            const child = children[i];
            if (child.key == key) {
                indexToDelete = i
            }
        }
        if (indexToDelete != -1) {
            this.children.splice(indexToDelete)
            let notifyData = { //通知事件
                key: key,
            }
            self.propertyDeleteSubject.next(notifyData)
            self.infoSubject.next(notifyData)
        }
    }
    /* 替换整个属性 */
    setValue(jsonObject) {
        let self = this

        let newObject = new ObjectDimension('root', jsonObject)
        this.children = newObject.children
        let notifyData = { //通知事件
            key: self.key,
            oldValue: null,
            newValue: this.children,
        }
        self.valueChangeSubject.next(notifyData)
        self.infoSubject.next(notifyData)
    }

    toJson() {
        if (!this.children) {
            this.children = []
        }
        let children = this.children
        let obj = {}
        children.forEach(child => {
            obj[child.key] = child.toJson()
        });
        return obj
    }
}

class ListDimension extends Dimension {
    constructor(key, value = null, space = null, accurators = null) {
        super(key, value, space, accurators)
        this.dtype = 'list'
        this.length = 0
        this.processListDType(value)
    }

    /* 准备事件 */
    prepareSubject() {
        this.valueChangeSubject = new Subject();
        this.pushSubject = new Subject();
        this.spliceSubject = new Subject();
        this.removeSubject = new Subject();
        this.spaceChangeSubject = new Subject();
        this.infoSubject = new Subject();
    }

    /* 整个数组进行替换 */
    setValue(listObject, headers) {
        let self = this

        let newList = new ListDimension('root', listObject)
        this.children = newList.children
        let notifyData = { //通知事件
            key: null,
            oldValue: null,
            newValue: this.children,
            headers: headers
        }
        self.valueChangeSubject.next(notifyData)
        self.infoSubject.next(notifyData)
    }

    /* 添加一个元素 */
    push(id, obj) {
        let self = this
        if (!this.children) {
            this.children = []
        }
        let children = this.children
        let newData = this.generateNewData(id, obj)
        children.push(newData)
        this.length = children.length
        let notifyData = { //通知事件
            push: children
        }
        self.pushSubject.next(notifyData)
        self.infoSubject.next(notifyData)
    }

    /* 根据参数来判断是哪种情况 
    1 参数两个，删除
    2 参数三个，替换
    */
    splice() { //动态参数
        let self = this
        this.children.splice(...arguments)
        let notifyData = { //通知事件
            splice: [...arguments]
        }
        self.spliceSubject.next(notifyData)
        self.infoSubject.next(notifyData)
    }
    /* 根据原数组内是否包含此id值的元素，确定是添加还是更新 */
    updateOrPushById(id, obj) {
        let isIn = false
        let index = -1
        if (!this.children) {
            this.children = []
        }
        let children = this.children
        children.forEach((child, i) => {
            if (child.id == id) {
                isIn = true
                index = i
            }
        });
        let newData = this.generateNewData(id, obj)
        // 判断类型
        if (index != -1) {
            this.splice(index, 1, newData)
        } else {
            this.push(id, obj)
        }
    }
    /* 获取第i个元素 */
    getIndex(i) {
        let result
        try {
            result = this.children[i]
        } catch (err) {
            console.log('无此索引值');
        }
        return result
    }
    /* 迭代所有子元素并执行回调函数 */
    forEach(callback) {
        this.children.forEach(function (item) {
            callback(item)
        })
    }

    toJson() {
        let children = this.children
        let list = []
        children.forEach(child => {
            list.push(child.toJson())
        });
        return list
    }
}
export {
    Dimension,
    LeafDimension,
    ObjectDimension,
    ListDimension
}